// @Author YaoYa
// @Date 2024-07-29 22:59:00
// @Desc
package api

import (
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/go-playground/validator/v10"
	"github.com/redis/go-redis/v9"
	"mxshop-api/user-web/middlewares"
	"mxshop-api/user-web/models"
	"strings"
	"time"

	"context"
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"mxshop-api/user-web/forms"
	"mxshop-api/user-web/global"
	"mxshop-api/user-web/global/reponse"
	"mxshop-api/user-web/proto"
)

type SignUpForm struct {
	Age        uint8  `json:"age" binding:"gte=1,lte=130"`
	Name       string `json:"name" binding:"required,min=3"`
	Email      string `json:"email" binding:"required,email"`
	Password   string `json:"password" binding:"required"`
	RePassword string `json:"re_password" binding:"required,eqfield=Password"` //跨字段
}

// SignUpForm.re_password 去除前缀
func removeTopStruct(fileds map[string]string) map[string]string {
	rsp := map[string]string{}
	for filed, err := range fileds {
		rsp[filed[strings.Index(filed, ".")+1:]] = err
	}
	return rsp
}

func HandlerGrpcErrorToHttp(err error, c *gin.Context) {
	//将grpc的code转换为http的状态码
	if err != nil {
		if e, ok := status.FromError(err); ok {
			switch e.Code() {
			case codes.NotFound:
				c.JSON(http.StatusNotFound, gin.H{
					"msg": e.Message(),
				})
			case codes.Internal:
				c.JSON(http.StatusInternalServerError, gin.H{
					"msg": "内部错误",
				})
			case codes.InvalidArgument:
				c.JSON(http.StatusBadRequest, gin.H{
					"msg": "返回参数错误",
				})
			case codes.Unavailable:
				c.JSON(http.StatusInternalServerError, gin.H{
					"msg": "用户服务不可用",
				})
			default:
				c.JSON(http.StatusInternalServerError, gin.H{
					"msg": "其他错误" + e.Message(),
				})
			}
			return
		}
	}
}

func HandleValidatorError(c *gin.Context, err error) {
	errs, ok := err.(validator.ValidationErrors)
	if !ok { //如果不是validator校验错误直接返回
		//invalid character 'm' looking for beginning of object key string
		//这种错误直接返回
		c.JSON(http.StatusOK, gin.H{
			"msg": err.Error(),
		})
		zap.S().Infof(err.Error())
		return //如果不加return后续逻辑还会执行,return结束当前方法
	}

	c.JSON(http.StatusBadRequest, gin.H{
		"error": removeTopStruct(errs.Translate(global.Trans)),
	})

}

func GetUserList(ctx *gin.Context) {
	//ip := "127.0.0.1"
	//port := 50051

	//获取当前用户token里的claims信息
	claims, _ := ctx.Get("claims")
	currentUser := claims.(*models.CustomClaims)

	zap.S().Infof("访问用户：%d", currentUser.ID)

	pn := ctx.DefaultQuery("pn", "0")
	pnInt, _ := strconv.Atoi(pn)
	pSize := ctx.DefaultQuery("pSize", "0")
	pSizeInt, _ := strconv.Atoi(pSize)

	rsp, err := global.UserSrvClient.GetUserList(context.Background(), &proto.PageInfo{
		Pn:    uint32(pnInt),
		PSize: uint32(pSizeInt),
	})
	if err != nil {
		zap.S().Errorw("[GetUserList] 查询 【用户列表】失败")
		HandlerGrpcErrorToHttp(err, ctx)
		return
	}

	result := make([]interface{}, 0)
	for _, value := range rsp.Data {
		//data := make(map[string]interface{})
		//data["id"] = value.Id
		//data["name"] = value.NickName
		//data["birthday"] = value.BirthDay
		//data["gender"] = value.Gender
		//data["mobile"] = value.Mobile

		data := reponse.UserResponse{
			Id:       value.Id,
			NickName: value.NickName,
			//BirthDay: time.Unix(int64(value.BirthDay), 0).Format("2006-01-01"),
			BirthDay: reponse.JsonTime(time.Unix(int64(value.BirthDay), 0)),
			Gender:   value.Gender,
			Mobile:   value.Mobile,
		}

		result = append(result, data)
	}

	ctx.JSON(http.StatusOK, result)

}

func PassWordLogin(c *gin.Context) {
	//表单验证
	passwordLoginForm := forms.PassWordLoginForm{}
	if err := c.ShouldBindJSON(&passwordLoginForm); err != nil {
		//如何返回错误信息
		HandleValidatorError(c, err)
		return
	}

	if !store.Verify(passwordLoginForm.CaptchaId, passwordLoginForm.Captcha, true) {
		c.JSON(http.StatusBadRequest, gin.H{
			"captcha": "验证码错误",
		})
		return
	}

	//注意点:多个c.JSON他会合并在一起,状态码为第一个

	//拨号连接用户grpc服务
	conn, err := grpc.Dial(fmt.Sprintf("%s:%d", global.ServerConfig.UserSrvInfo.Host, global.ServerConfig.UserSrvInfo.Port), grpc.WithInsecure())
	if err != nil { //为什么服务端没有启动 err还是不为nil
		zap.S().Errorw("[GetUserList] 连接 【用户服务】失败",
			"msg", err.Error(),
		)
	}

	//生成grpc的client并调用接口
	client := proto.NewUserClient(conn)

	//登录的逻辑
	if rsp, err := client.GetUserByMobile(context.Background(), &proto.MobileRequest{
		Mobile: passwordLoginForm.Mobile,
	}); err != nil {
		if e, ok := status.FromError(err); ok {
			switch e.Code() {
			case codes.NotFound:
				c.JSON(http.StatusBadRequest, map[string]string{
					"mobile": "用户不存在",
				})
			default:
				c.JSON(http.StatusInternalServerError, map[string]string{
					"mobile": "登录失败",
				})
			}
		}
		return
	} else {
		//校验密码是否正确
		if passRsp, pasErr := client.CheckPassWord(context.Background(), &proto.PasswordCheckInfo{
			Password:          passwordLoginForm.PassWord,
			EncryptedPassword: rsp.Password,
		}); pasErr != nil {
			c.JSON(http.StatusInternalServerError, map[string]string{
				"password": "登录失败",
			})
			return
		} else {
			//登陆成功的机制
			if passRsp.Success {
				//生成token
				j := middlewares.NewJWT()
				token, err := j.CreateToken(models.CustomClaims{
					ID:          uint(rsp.Id),
					NickName:    rsp.NickName,
					AuthorityId: uint(rsp.Role),
					StandardClaims: jwt.StandardClaims{
						ExpiresAt: time.Now().Unix() + 60*60*24*30, //30天过期
						Issuer:    "imooc",                         //签发机构
						NotBefore: time.Now().Unix(),               //签名的生效时间
					},
				})
				if err != nil {
					c.JSON(http.StatusInternalServerError, map[string]string{
						"msg": "生成token失败",
					})
					return
				}

				c.JSON(http.StatusOK, gin.H{
					"id":         rsp.Id,
					"nike_name":  rsp.NickName,
					"token":      token,
					"expires_at": (time.Now().Unix() + 60*60*24*30) * 1000,
				})
			} else {
				c.JSON(http.StatusBadRequest, map[string]string{
					"msg": "登录失败",
				})
			}
		}
	}

}

func Register(c *gin.Context) {
	//用户注册
	registerForm := forms.RegisterForm{}
	if err := c.ShouldBindJSON(&registerForm); err != nil {
		HandleValidatorError(c, err)
		return
	}

	//查询当时存储到redis里的code
	rdb := redis.NewClient(&redis.Options{
		Addr:     fmt.Sprintf("%s:%d", global.ServerConfig.RedisInfo.Host, global.ServerConfig.RedisInfo.Port),
		Password: "", // no password set
		DB:       0,  // use default DB
		Protocol: 3,  // specify 2 for RESP 2 or 3 for RESP 3
	})
	code, err := rdb.Get(context.Background(), registerForm.Mobile).Result()
	if err == redis.Nil {
		zap.S().Errorf("key 不存在")
		c.JSON(http.StatusBadRequest, gin.H{
			"code": "验证码错误",
		})
		return
	} else {
		if code != registerForm.Code {
			zap.S().Errorf("验证码与redis中不匹配")
			c.JSON(http.StatusBadRequest, gin.H{
				"code": "验证码错误",
			})
			return
		}
	}

	//拨号连接用户grpc服务
	conn, err := grpc.Dial(fmt.Sprintf("%s:%d", global.ServerConfig.UserSrvInfo.Host, global.ServerConfig.UserSrvInfo.Port), grpc.WithInsecure())
	if err != nil { //为什么服务端没有启动 err还是不为nil
		zap.S().Errorw("[GetUserList] 连接 【用户服务】失败",
			"msg", err.Error(),
		)
	}

	//生成grpc的client并调用接口
	client := proto.NewUserClient(conn)

	//创建用户信息
	user, err := client.CreateUser(context.Background(), &proto.CreateUserInfo{
		NickName: registerForm.Mobile,
		PassWord: registerForm.PassWord,
		Mobile:   registerForm.Mobile,
	})
	if err != nil {
		zap.S().Errorw("[CreateUser] 查询 【新建用户】失败:%s", err.Error())
		HandlerGrpcErrorToHttp(err, c)
		return
	}

	//生成token
	j := middlewares.NewJWT()
	token, err := j.CreateToken(models.CustomClaims{
		ID:          uint(user.Id),
		NickName:    user.NickName,
		AuthorityId: uint(user.Role),
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: time.Now().Unix() + 60*60*24*30, //30天过期
			Issuer:    "imooc",                         //签发机构
			NotBefore: time.Now().Unix(),               //签名的生效时间
		},
	})
	if err != nil {
		c.JSON(http.StatusInternalServerError, map[string]string{
			"msg": "生成token失败",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"id":         user.Id,
		"nike_name":  user.NickName,
		"token":      token,
		"expires_at": (time.Now().Unix() + 60*60*24*30) * 1000,
	})

}
